Quick responsive type-ahead function

For an XPages form I needed a workaround for a combobox with a large data-set. At first I tried the select2 jQuery plugin so I could also have a filter option as some form of type-ahead. However it turned out that my select2 list became quiet unresponsive when an entry far in the list was selected (generating the list and filtering was fine).

So my next option was to go for a type-ahead approach. First hit on Google was a NotesIn9 vid about a fancy type based upon the original post of Tim Tripcony (hello 2009).

Due to the large data-set instead of the getAllEntriesByKey method I decided to go for a getEntryByKey method and from the found view entry continue with createViewNavFrom. This turned out to be extremely responsive!

Further I noticed in the original code a hash object is used so that does not guaranteed me a correct order of my result list so I added an additional array, just for a returning a list with correct sort order.

I like especially in this approach the option to control the returned markup so think here about Bootstrap Media object lists. And of course the ease to set the value that you want to have returned in the edit box.

This is not my first blog-post on delivering search function to your application so now you have just another solution available for your toolbox.

The script is available as a gist.

Happy development =)

 

Get rid of “Infinity” as the result of a @DbColumn in a combobox – Quicktip

Who has not experienced the following:

XPages has a 32 or 64 ( can’t remember which ) limit for the lookup formula whereas in Notes the limit is only for the whole of the formula e.g. after @unique is applied.

It makes it very hard to have meaningful drop downs for filtering data.

Instead you can use SSJS e.g.:

var lookupView:NotesView = database.getView(“<LOOKUPVIEW>”);
lookupView.getColumnValues(0)

This is the equivalent to:

@DbColumn( “”,”<LOOKUPVIEW>”, 1 );

and returns you the full result without the limit.

But this also brings me to the following problem:

When I enable Select2 on a loooong combobox the list is populated the type-ahead / filtering works fine BUT when I select a value long down the list the response of the select2 combobox becomes really sloooow.

Does anyone has an approach to this?

Hiding Notes links for Mobile Web users

Probably you are working in a collaborative environment where users access their beloved Notes apps with multiple clients: The Notes client, The Notes Browser plugin, Web browser and mobile devices.

Not all Notes applications have defined business cases in which they should become web-enabled. This could be because of application complexity or that the life-cycle / ownership of the application is poorly defined.

Nevertheless it is hard to prohibit that links to these applications or documents in them are being published on web-pages (probably you want to promote the distribution of information). Luckily there is that lovely tool called the IBM Notes Browser Plugin but this plugin is restricted to normal web browsers and not to all of them.

And then you have your ‘leading’ technology adapting usergroup that prefer to access Notes data in app and web-page form. For them the Notes Browser Plugin is not available so why should you bother to show them links to documents in applications that have not been web-enabled yet?

The following script will hide these Notes link on web-pages  and replace them with just text. It uses the deviceBean which provides an easy to use and easy to program way of identifying a popular range of mobile and tablet devices.

var isMobile = ‘#{javascript:return deviceBean.isMobile()}’;
if (isMobile==’true’){
$( document ).ready(function() {
$(“a[href^=’Notes://’]”).each(function () {
$(this).replaceWith($(this).text());
});
});
}

Add it to the onClientLoad event of your XPage.

Angular playground: applying an infinitescroll for Domino Access Services

Intro

Inspired by a serie of blogposts from Marky Roden and popularity in the web dev community I decided to step (once again) from the XPages path (a trend?) and feed my curiosity on AngularJS. An excellent stepping stone is the example database provided by Marky and the AngularJS Fundamentals In 60-ish Minutes presentation by Dan Wahlin.

Domino Access Services

The example database contains examples for CRUD operations via Domino Access Services (way to go!) but is limited in the display of documents from a list/view. The default number of documents returned for view/folder entries is set to 10. Not much of value for a real world application. So either I had to provide some sort of pagination or look for more smartphone/tablet common feature: infinite scroll.

The pagination examples I found only handled a one time loaded data-set and a proper application can contain thousands of records (I find it a common pattern that customers underestimate the number of documents that are being created in a Notes application). Angular can be extended with custom directives and ngInfiniteScroll is such a great example.

So what did I need to do to get this directive applied to the example database?

Implementation

index.html

First I installed the ng-infinite-scroll.min script in my database and updated the header section in index.html

I also needed to include the complete jQuery library because Angular has jQuery lite built in which doesn’t have seem to have the features for dynamic height. You should also load the jQuery script before jQlite.

app.js

Next you need to register the infinitescroll directive for my angular application:

var personApp = angular.module(‘personApp’, [
‘ngRoute’,
‘peopleControllers’,
‘infinite-scroll’
]);

factory

The examples for ngInfiniteScroll demonstrate a factory which is not used in the example database. From my limited knowledge on Angular I have understood that a factory is an injectable function.

personApp.factory(‘DAS’, function($http) {

var DAS = function() {
this.items = [];
this.busy = false;
this.after = 0;
this.count = 30;
this.order = ‘firstname’;

};

DAS.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = ‘//dev1/apps/others/angular/ainx.nsf/api/data/collections/name/byFirstName5Col?open’ + ‘&count=’ + this.count + ‘&start=’ + this.after;

$http.get(url).success(function(data) {
var items = data;
for (var i = 0; i < items.length; i++) {
this.items.push(items[i]);
}
this.after = this.after + this.count;
this.busy = false;
}.bind(this));
};
return DAS;
});

My factory is called DAS (I guess I break here the naming convention). You can store your factories in a separate (new) file e.g. main.js.

Controller.js

I removed the existing PeopleListCtrl controller and replaced it with the following one:

personApp.controller(‘PeopleListCtrl’, function($scope, DAS) {
$scope.DAS = new DAS();
});

partial-list.html

With everything in place I now needed to update the display of the list, which is defined in a partial. Besides using the factory I also wanted to add some additional features such as search and sorting. This turned out to be really simple.

At the end I wanted to have something as followed:

Screenshot_3

 

Search & Sorting

For a search feature I added an input control and used the directive ngModel called query to apply a filtering via a search query

Search:

<input ng-model=”query” placeholder=”Search for person” autofocus class=”input-medium search-query”/>

For a sorting feature I added a select control and bound that to the order property for the data via a custom directive. Further I added a radio-button group and used the directive ngModel called direction:

Sorting:
<select ng-model="DAS.order“>
First Name
Last Name
ZIP

<input type="radio" ng-model=”direction” name=”direction” checked> ascending

<input type="radio" ng-model=”direction” name=”direction” value=”reverse”> descending

Note that the default sorting is set to ‘firstname’ in the factory.

Apply infinitescroll to the data-table

Finally we need to apply the nextPage function in the DAS function by wrapping the data-table with a div and add the infinite-scroll attribute to it.

<div infinite-scroll=”DAS.nextPage()” infinite-scroll-distance=”3″>

Then we use the ng-repeat directive and for each item or person in the data we display a new row. Here is also were we apply the filter and sorting options:

…<tr ng-repeat=”person in DAS.items | filter:query | orderBy:DAS.order:direction”>

Position First Name Last Name Zip
{{person[“@position”]}} {{person.firstname}} {{person.lastname}} {{person.zip}} Edit Delete

<div ng-show=’DAS.busy’>Loading data…